import com.sun.tools.javac.Main;
import element.base.Synt;
import element.base.Word;
import element.base.WordType;
import element.synt.*;

import java.util.ArrayList;

public class Parser {
    private ArrayList<Word> words;
    private int pointer;
    private Word nowWord;

    public Parser(ArrayList<Word> words) {
        this.words = words;
        this.pointer = 0;
        this.nowWord = null;
    }

    public void getWord() {
        if (pointer < words.size()) {
            nowWord = words.get(pointer++);
        }
        return;
    }

    public void rollBack(int n) {
        for (int i = 0; i < n; i++) {
            pointer--;
        }
        nowWord = words.get(pointer - 1);
    }

    public Synt Execute() {
        getWord();
        return Program();
    }

    //Program ::= [<Global>] <MainFunction>
    public Program Program() {
        Program program = new Program();
        if (!nowWord.getType().equals(WordType.MAINTK)) {
            Global global = Global();
            program.addNode(global);
        }

        MainFuncDef mainFuncdef = MainFuncDef();
        program.addNode(mainFuncdef);

        return program;
    }

    //Global ::= {<GlobalFactor>}
    public Global Global() {
        Global global = new Global();
        while (true) {
            if (!nowWord.getType().equals(WordType.MAINTK)) {
                GlobalFactor globalFactor = GlobalFactor();
                global.addNode(globalFactor);
            } else {
                break;
            }
        }

        return global;
    }

    //GlobalFactor ::= <ClassDef> | <FuncDef> | <Decl>
    public GlobalFactor GlobalFactor() {
        GlobalFactor globalFactor = new GlobalFactor();
        if (nowWord.getType().equals(WordType.CLASSTK)) {
            ClassDef classDef = ClassDef();
            globalFactor.addNode(classDef);
        } else if (nowWord.getType().equals(WordType.DEFTK)) {
            FuncDef funcDef = FuncDef();
            globalFactor.addNode(funcDef);
        } else {
            Decl decl = Decl();
            globalFactor.addNode(decl);
        }

        return globalFactor;
    }

    //Decl ::= <ConstDecl> | <VarDecl>
    public Decl Decl() {
        Decl decl = new Decl();
        if (nowWord.getType().equals(WordType.CONSTTK)) {
            ConstDecl constDecl = ConstDecl();
            decl.addNode(constDecl);
        } else {
            VarDecl varDecl = VarDecl();
            decl.addNode(varDecl);
        }

        return decl;
    }

    //ConstDecl ::= 'const' <Type> <ConstDef> {',' <ConstDef>} ';'
    public ConstDecl ConstDecl() {
        ConstDecl constDecl = new ConstDecl();

        constDecl.addNode(nowWord);
        getWord();

        Type type = Type();
        constDecl.addNode(type);

        ConstDef constDef = ConstDef();
        constDecl.addNode(constDef);

        while (nowWord.getType().equals(WordType.COMMA)) {
            constDecl.addNode(nowWord);
            getWord();
            ConstDef constDef1 = ConstDef();
            constDecl.addNode(constDef1);
        }

        constDecl.addNode(nowWord);
        getWord();

        return constDecl;
    }

    //Type ::= 'int' | 'double' | 'char' | 'long' | 'longlong' | 'bool' | 'string' | <FuncType>
    //            | <ClassType>
    public Type Type() {
        Type type = new Type();
        if (nowWord.getType().equals(WordType.IDENFR)) {
            ClassType classType = ClassType();
            type.addNode(classType);
        } else if (nowWord.getType().equals(WordType.FUNCTK)) {
            FuncType funcType = FuncType();
            type.addNode(funcType);
        } else {
            type.addNode(nowWord);
            getWord();
        }

        return type;
    }

    //FuncType ::= 'func' '<' <Type> '>'
    public FuncType FuncType() {
        FuncType funcType = new FuncType();
        funcType.addNode(nowWord);
        getWord();
        funcType.addNode(nowWord);
        getWord();
        Type type = Type();
        funcType.addNode(type);
        if (!nowWord.getType().equals(WordType.GRE)) {
            ArrayLengths arrayLengths = ArrayLengths();
            funcType.addNode(arrayLengths);
        }
        funcType.addNode(nowWord);
        getWord();
        return funcType;
    }

    //ClassType ::= <Ident>
    public ClassType ClassType() {
        ClassType classType = new ClassType();
        classType.addNode(nowWord);
        getWord();

        return classType;
    }

    //ConstDef ::= <Ident> [<ArrayLengths>] '=' <ConstInitVal>
    public ConstDef ConstDef() {
        ConstDef constDef = new ConstDef();
        constDef.addNode(nowWord);
        getWord();

        if (nowWord.getType().equals(WordType.LBRACK)) {
            ArrayLengths arrayLengths = ArrayLengths();
            constDef.addNode(arrayLengths);
        }

        constDef.addNode(nowWord);
        getWord();

        ConstInitVal constInitVal = ConstInitVal();
        constDef.addNode(constInitVal);

        return constDef;
    }

    //ArrayLengths ::= '[' <ConstExp> {',' <ConstExp>} ']'
    public ArrayLengths ArrayLengths() {
        ArrayLengths arrayLengths = new ArrayLengths();
        arrayLengths.addNode(nowWord);
        getWord();

        Exp constExp = Exp();
        arrayLengths.addNode(constExp);

        while (nowWord.getType().equals(WordType.COMMA)) {
            arrayLengths.addNode(nowWord);
            getWord();
            Exp constExp1 = Exp();
            arrayLengths.addNode(constExp1);
        }

        arrayLengths.addNode(nowWord);
        getWord();

        return arrayLengths;
    }

    //ConstInitVal ::= <ConstExp> | '{' [<ConstInitVal> {',' <ConstInitVal>}] '}'
    public ConstInitVal ConstInitVal() {
        ConstInitVal constInitVal = new ConstInitVal();
        if (nowWord.getType().equals(WordType.LBRACE)) {
            constInitVal.addNode(nowWord);
            getWord();

            if (!nowWord.getType().equals(WordType.RBRACE)) {
                ConstInitVal constInitVal1 = ConstInitVal();
                constInitVal.addNode(constInitVal1);

                while (nowWord.getType().equals(WordType.COMMA)) {
                    constInitVal.addNode(nowWord);
                    getWord();

                    ConstInitVal constInitVal2 = ConstInitVal();
                    constInitVal.addNode(constInitVal2);
                }
            }

            constInitVal.addNode(nowWord);
            getWord();
        } else {
            Exp constExp = Exp();
            constInitVal.addNode(constExp);
        }

        return constInitVal;
    }

    //VarDecl ::= <Type> <VarDef> {',' <VarDef>} ';'
    public VarDecl VarDecl() {
        VarDecl varDecl = new VarDecl();

        Type type = Type();
        varDecl.addNode(type);

        VarDef varDef = VarDef();
        varDecl.addNode(varDef);

        while (nowWord.getType().equals(WordType.COMMA)) {
            varDecl.addNode(nowWord);
            getWord();

            VarDef varDef1 = VarDef();
            varDecl.addNode(varDef1);
        }

        varDecl.addNode(nowWord);
        getWord();

        return varDecl;
    }

    //VarDef ::= <Ident> [<ArrayLengths>] | <Ident> [<ArrayLengths>] '=' <InitVal>
    public VarDef VarDef() {
        VarDef varDef = new VarDef();

        varDef.addNode(nowWord);
        getWord();

        if (nowWord.getType().equals(WordType.LBRACK)) {
            ArrayLengths arrayLengths = ArrayLengths();
            varDef.addNode(arrayLengths);
        }

        if (nowWord.getType().equals(WordType.ASSIGN)) {
            varDef.addNode(nowWord);
            getWord();

            InitVal initVal = InitVal();
            varDef.addNode(initVal);
        }

        return varDef;
    }

    //InitVal ::= <Exp> | '{' [<InitVal> {',' <InitVal>}] '}'
    public InitVal InitVal() {
        InitVal initVal = new InitVal();
        if (nowWord.getType().equals(WordType.LBRACE)) {
            initVal.addNode(nowWord);
            getWord();

            if (!nowWord.getType().equals(WordType.RBRACE)) {
                InitVal initVal1 = InitVal();
                initVal.addNode(initVal1);

                while (nowWord.getType().equals(WordType.COMMA)) {
                    initVal.addNode(nowWord);
                    getWord();

                    InitVal initVal2 = InitVal();
                    initVal.addNode(initVal2);
                }
            }

            initVal.addNode(nowWord);
            getWord();
        } else {
            Exp exp = Exp();
            initVal.addNode(exp);
        }

        return initVal;
    }

    //FuncDef ::= 'def' <Ident> '(' [<FuncFParams>] ')' '->' (<Type> | 'void') [<ArrayLengths>] <Block>
    public FuncDef FuncDef() {
        FuncDef funcDef = new FuncDef();
        funcDef.addNode(nowWord);
        getWord();

        funcDef.addNode(nowWord);
        getWord();

        funcDef.addNode(nowWord);
        getWord();

        if (!nowWord.getType().equals(WordType.RPARENT)) {
            FuncFParams funcFParams = FuncFParams();
            funcDef.addNode(funcFParams);
        }

        funcDef.addNode(nowWord);
        getWord();

        funcDef.addNode(nowWord);
        getWord();

        if (nowWord.getType().equals(WordType.VOIDTK)) {
            funcDef.addNode(nowWord);
            getWord();
        } else {
            Type type = Type();
            funcDef.addNode(type);
        }

        if (nowWord.getType().equals(WordType.LBRACK)) {
            ArrayLengths arrayLengths = ArrayLengths();
            funcDef.addNode(arrayLengths);
        }

        Block block = Block();
        funcDef.addNode(block);

        return funcDef;
    }

    //FuncFParams ::= <FuncFParam> {',' <FuncFParam>}
    public FuncFParams FuncFParams() {
        FuncFParams funcFParams = new FuncFParams();

        FuncFParam funcFParam = FuncFParam();
        funcFParams.addNode(funcFParam);

        while (nowWord.getType().equals(WordType.COMMA)) {
            funcFParams.addNode(nowWord);
            getWord();

            FuncFParam funcFParam1 = FuncFParam();
            funcFParams.addNode(funcFParam1);
        }

        return funcFParams;
    }

    //FuncFParam ::= <Type> <Ident> [ '[' {',' <ConstExp>} ']' ]
    public FuncFParam FuncFParam() {
        FuncFParam funcFParam = new FuncFParam();

        Type type = Type();
        funcFParam.addNode(type);

        funcFParam.addNode(nowWord);
        getWord();

        if (nowWord.getType().equals(WordType.LBRACK)) {
            funcFParam.addNode(nowWord);
            getWord();

            while (nowWord.getType().equals(WordType.COMMA)) {
                funcFParam.addNode(nowWord);
                getWord();

                Exp constExp = Exp();
                funcFParam.addNode(constExp);
            }

            funcFParam.addNode(nowWord);
            getWord();
        }

        return funcFParam;
    }

    //ClassDef ::= 'Class' <Ident> '{' <ClassInner> '}'
    public ClassDef ClassDef() {
        ClassDef classDef = new ClassDef();

        classDef.addNode(nowWord);
        getWord();

        classDef.addNode(nowWord);
        getWord();

        classDef.addNode(nowWord);
        getWord();

        ClassInner classInner = ClassInner();
        classDef.addNode(classInner);

        classDef.addNode(nowWord);
        getWord();

        return classDef;
    }

    //ClassInner ::= {<Decl>} {<Annotate>} {<ConstructorDef>} {<FuncDef>}
    public ClassInner ClassInner() {
        ClassInner classInner = new ClassInner();

        if (nowWord.getType().equals(WordType.RBRACE)) {
            return classInner;
        }

        while (!nowWord.getType().equals(WordType.AT) &&
                !nowWord.getType().equals(WordType.CONSTRUCTTK) &&
                !nowWord.getType().equals(WordType.DEFTK) &&
                !nowWord.getType().equals(WordType.RBRACE)) {
            Decl decl = Decl();
            classInner.addNode(decl);
        }

        while (nowWord.getType().equals(WordType.AT)) {
            Annotate annotate = Annotate();
            classInner.addNode(annotate);
        }

        while (nowWord.getType().equals(WordType.CONSTRUCTTK)) {
            ConstructorDef constructorDef = ConstructorDef();
            classInner.addNode(constructorDef);
        }

        while (nowWord.getType().equals(WordType.DEFTK)) {
            FuncDef funcDef = FuncDef();
            classInner.addNode(funcDef);
        }

        return classInner;
    }

    //Annotate ::= '@' <AnnotateType> ';'
    public Annotate Annotate() {
        Annotate annotate = new Annotate();

        annotate.addNode(nowWord);
        getWord();

        AnnotateType annotateType = AnnotateType();
        annotate.addNode(annotateType);

        annotate.addNode(nowWord);
        getWord();

        return annotate;
    }

    //AnnotateType ::= 'AllAttrConstructor' | 'NoAttrConstructor'
    public AnnotateType AnnotateType() {
        AnnotateType annotateType = new AnnotateType();
        annotateType.addNode(nowWord);
        getWord();
        return annotateType;
    }

    //ConstructorDef ::= 'constructor' '(' [FuncFParams] ')' <Block>
    public ConstructorDef ConstructorDef() {
        ConstructorDef constructorDef = new ConstructorDef();

        constructorDef.addNode(nowWord);
        getWord();

        constructorDef.addNode(nowWord);
        getWord();

        if (!nowWord.getType().equals(WordType.RPARENT)) {
            FuncFParams funcFParams = FuncFParams();
            constructorDef.addNode(funcFParams);
        }

        constructorDef.addNode(nowWord);
        getWord();

        Block block = Block();
        constructorDef.addNode(block);

        return constructorDef;
    }

    //MainFuncDef ::= 'Main' <Block>
    public MainFuncDef MainFuncDef() {
        MainFuncDef mainFuncDef = new MainFuncDef();

        mainFuncDef.addNode(nowWord);
        getWord();

        Block block = Block();
        mainFuncDef.addNode(block);

        return mainFuncDef;
    }

    //Block ::= '{' {BlockItem} '}'
    public Block Block() {
        Block block = new Block();

        block.addNode(nowWord);
        getWord();

        while (!nowWord.getType().equals(WordType.RBRACE)) {
            BlockItem blockItem = BlockItem();
            block.addNode(blockItem);
        }

        block.addNode(nowWord);
        getWord();

        return block;
    }

    //BlockItem ::= <Decl> | <Stmt>
    public BlockItem BlockItem() {
        BlockItem blockItem = new BlockItem();

        if (nowWord.getType().equals(WordType.CONSTTK) ||
                nowWord.getType().equals(WordType.INTTK) ||
                nowWord.getType().equals(WordType.LONGTK) ||
                nowWord.getType().equals(WordType.LLTK) ||
                nowWord.getType().equals(WordType.DOUBLETK) ||
                nowWord.getType().equals(WordType.CHARTK) ||
                nowWord.getType().equals(WordType.STRINGTK) ||
                nowWord.getType().equals(WordType.BOOLTK) ||
                nowWord.getType().equals(WordType.FUNCTK)) {
            Decl decl = Decl();
            blockItem.addNode(decl);
        } else if (nowWord.getType().equals(WordType.IDENFR)) {
            if (words.get(pointer).getType().equals(WordType.IDENFR)) {
                Decl decl = Decl();
                blockItem.addNode(decl);
            } else {
                Stmt stmt = Stmt();
                blockItem.addNode(stmt);
            }
        } else {
            Stmt stmt = Stmt();
            blockItem.addNode(stmt);
        }

        return blockItem;
    }

    //Stmt ::= <LVal> <Assign> <Exp> ';' |
    //             [<Exp>] ';' |
    //             <LVal> '+' '+' ';' |
    //             <LVal> '-' '-' ';' |
    //             <Block> |
    //             'if' '(' <Exp> ')' <Stmt> ['else' <Stmt>] |
    //             'while' '(' <Exp> ')' <Stmt> |
    //             'for' '(' <SimpleStmt> ';' <Exp> ';' <SimpleStmt> ')' <Stmt> |
    //             'break' ';' |
    //             'continue' ';' |
    //             'return' <Exp> ';' |
    //             <LVal> '=' 'input' '(' <String> ')' ';'
    //             'print' '(' <String> ['.' 'format' '(' <Exp> {',' <Exp>} ')'] ')'
    public Stmt Stmt() {
        Stmt stmt = new Stmt();

        if (nowWord.getType().equals(WordType.IFTK)) {
            stmt.addNode(nowWord);
            getWord();
            stmt.addNode(nowWord);
            getWord();
            Exp exp = Exp();
            stmt.addNode(exp);
            stmt.addNode(nowWord);
            getWord();
            Stmt stmt1 = Stmt();
            stmt.addNode(stmt1);
            if (nowWord.getType().equals(WordType.ELSETK)) {
                stmt.addNode(nowWord);
                getWord();
                Stmt stmt2 = Stmt();
                stmt.addNode(stmt2);
            }
        } else if (nowWord.getType().equals(WordType.WHILETK)) {
            stmt.addNode(nowWord);
            getWord();
            stmt.addNode(nowWord);
            getWord();
            Exp exp = Exp();
            stmt.addNode(exp);
            stmt.addNode(nowWord);
            getWord();
            Stmt stmt1 = Stmt();
            stmt.addNode(stmt1);
        } else if (nowWord.getType().equals(WordType.FORTK)) {
            stmt.addNode(nowWord);
            getWord();
            stmt.addNode(nowWord);
            getWord();
            SimpleStmt simpleStmt = SimpleStmt();
            stmt.addNode(simpleStmt);
            stmt.addNode(nowWord);
            getWord();
            if (!nowWord.getType().equals(WordType.SEMICN)) {
                Exp exp = Exp();
                stmt.addNode(exp);
            }
            stmt.addNode(nowWord);
            getWord();
            SimpleStmt simpleStmt1 = SimpleStmt();
            stmt.addNode(simpleStmt1);
            stmt.addNode(nowWord);
            getWord();
            Stmt stmt1 = Stmt();
            stmt.addNode(stmt1);
        } else if (nowWord.getType().equals(WordType.BREAKTK)) {
            stmt.addNode(nowWord);
            getWord();
            stmt.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.CONTINUETK)) {
            stmt.addNode(nowWord);
            getWord();
            stmt.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.RETURNTK)) {
            stmt.addNode(nowWord);
            getWord();
            Exp exp = Exp();
            stmt.addNode(exp);
            stmt.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.PRINTTK)) {
            stmt.addNode(nowWord);
            getWord();
            stmt.addNode(nowWord);
            getWord();
            stmt.addNode(nowWord);
            getWord();
            if (nowWord.getType().equals(WordType.POINT)) {
                stmt.addNode(nowWord);
                getWord();
                stmt.addNode(nowWord);
                getWord();
                stmt.addNode(nowWord);
                getWord();
                Exp exp = Exp();
                stmt.addNode(exp);
                while (nowWord.getType().equals(WordType.COMMA)) {
                    stmt.addNode(nowWord);
                    getWord();
                    Exp exp1 = Exp();
                    stmt.addNode(exp1);
                }
                stmt.addNode(nowWord);
                getWord();
            }
            stmt.addNode(nowWord);
            getWord();
            stmt.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.LBRACE)) {
            Block block = Block();
            stmt.addNode(block);
        } else if (nowWord.getType().equals(WordType.SEMICN)) {
            stmt.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.NUMBER) ||
                nowWord.getType().equals(WordType.CHARCON) ||
                nowWord.getType().equals(WordType.STRCON) ||
                nowWord.getType().equals(WordType.TRUETK) ||
                nowWord.getType().equals(WordType.FALSETK) ||
                nowWord.getType().equals(WordType.LPARENT) ||
                nowWord.getType().equals(WordType.ADD) ||
                nowWord.getType().equals(WordType.MINU)) {
            Exp exp = Exp();
            stmt.addNode(exp);
            stmt.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.IDENFR)) {
            int safe = 0;
            boolean assigner = false;
            boolean pusher = false;
            for (int i = pointer;;i++) {
                if (words.get(i).getType().equals(WordType.ASSIGN) ||
                        words.get(i).getType().equals(WordType.ADDASSIGN) ||
                        words.get(i).getType().equals(WordType.MINUASSIGN) ||
                        words.get(i).getType().equals(WordType.MULTASSIGN) ||
                        words.get(i).getType().equals(WordType.DIVASSIGN) ||
                        words.get(i).getType().equals(WordType.MODASSIGN)) {
                    assigner = true;
                    break;
                } else if (words.get(i).getType().equals(WordType.ADD) &&
                        words.get(i + 1).getType().equals(WordType.ADD) &&
                        words.get(i + 2).getType().equals(WordType.SEMICN)) {
                    pusher = true;
                    break;
                } else if (words.get(i).getType().equals(WordType.MINU) &&
                        words.get(i + 1).getType().equals(WordType.MINU) &&
                        words.get(i + 2).getType().equals(WordType.SEMICN)) {
                    pusher = true;
                    break;
                } else if (words.get(i).getType().equals(WordType.LBRACK)) {
                    safe++;
                } else if (words.get(i).getType().equals(WordType.RBRACK)) {
                    safe--;
                } else if (words.get(i).getType().equals(WordType.SEMICN) || i == words.size() - 1) {
                    break;
                } else {
                    if (safe == 0 && words.get(i).getType() != WordType.POINT && words.get(i).getType() != WordType.IDENFR) {
                        break;
                    }
                }
            }

            if (assigner) {
                LVal lVal = LVal();
                stmt.addNode(lVal);

                if (words.get(pointer).getType().equals(WordType.INPUTTK)) {
                    stmt.addNode(nowWord);
                    getWord();
                    stmt.addNode(nowWord);
                    getWord();
                    stmt.addNode(nowWord);
                    getWord();
                    if (nowWord.getType().equals(WordType.STRCON)) {
                        stmt.addNode(nowWord);
                        getWord();
                    }
                    stmt.addNode(nowWord);
                    getWord();
                    stmt.addNode(nowWord);
                    getWord();
                } else {
                    Assign assign = Assign();
                    stmt.addNode(assign);
                    Exp exp = Exp();
                    stmt.addNode(exp);
                    stmt.addNode(nowWord);
                    getWord();
                }
            } else if (pusher) {
                LVal lVal = LVal();
                stmt.addNode(lVal);

                stmt.addNode(nowWord);
                getWord();
                stmt.addNode(nowWord);
                getWord();
                stmt.addNode(nowWord);
                getWord();
            } else {
                Exp exp = Exp();
                stmt.addNode(exp);

                stmt.addNode(nowWord);
                getWord();
            }
        }

        return stmt;
    }

    //Assign ::= '=' | '+=' | '-=' | '*=' | '/=' | '%='
    public Assign Assign() {
        Assign assign = new Assign();
        assign.addNode(nowWord);
        getWord();

        return assign;
    }

    //Exp ::= <LorExp>
    public Exp Exp() {
        Exp exp = new Exp();

        LOrExp lorExp = LOrExp();
        exp.addNode(lorExp);

        return exp;
    }

    //SimpleStmt ::= <LVal> <Assign> <Exp> | [<Exp>] | <LVal> '++' | <LVal> '--'
    public SimpleStmt SimpleStmt() {
        SimpleStmt simpleStmt = new SimpleStmt();

        if (nowWord.getType().equals(WordType.SEMICN) || nowWord.getType().equals(WordType.RPARENT)) {
            return simpleStmt;
        }

        int safe = 0;
        boolean assigner = false;
        boolean pusher = false;
        for (int i = pointer;;i++) {
            if (words.get(i).getType().equals(WordType.ASSIGN) ||
                    words.get(i).getType().equals(WordType.ADDASSIGN) ||
                    words.get(i).getType().equals(WordType.MINUASSIGN) ||
                    words.get(i).getType().equals(WordType.MULTASSIGN) ||
                    words.get(i).getType().equals(WordType.DIVASSIGN) ||
                    words.get(i).getType().equals(WordType.MODASSIGN)) {
                assigner = true;
                break;
            } else if (words.get(i).getType().equals(WordType.ADD) &&
                    words.get(i + 1).getType().equals(WordType.ADD) &&
                    (words.get(i + 2).getType().equals(WordType.SEMICN) || words.get(i + 2).getType().equals(WordType.RPARENT))) {
                pusher = true;
                break;
            } else if (words.get(i).getType().equals(WordType.MINU) &&
                    words.get(i + 1).getType().equals(WordType.MINU) &&
                    (words.get(i + 2).getType().equals(WordType.SEMICN) || words.get(i + 2).getType().equals(WordType.RPARENT))) {
                pusher = true;
                break;
            } else if (words.get(i).getType().equals(WordType.LBRACK)) {
                safe++;
            } else if (words.get(i).getType().equals(WordType.RBRACK)) {
                safe--;
            } else if (words.get(i).getType().equals(WordType.SEMICN) || i == words.size() - 1) {
                break;
            } else {
                if (safe == 0 && words.get(i).getType() != WordType.POINT && words.get(i).getType() != WordType.IDENFR) {
                    break;
                }
            }
        }

        if (assigner) {
            LVal lVal = LVal();
            simpleStmt.addNode(lVal);
            Assign assign = Assign();
            simpleStmt.addNode(assign);
            Exp exp = Exp();
            simpleStmt.addNode(exp);
        } else if (pusher) {
            LVal lVal = LVal();
            simpleStmt.addNode(lVal);
            simpleStmt.addNode(nowWord);
            getWord();
            simpleStmt.addNode(nowWord);
            getWord();
        } else {
            Exp exp = Exp();
            simpleStmt.addNode(exp);
        }

        return simpleStmt;
    }

    //LVal ::= <Ident> [<ArrayPFix>] [<ClassPFix>]
    public LVal LVal() {
        LVal lVal = new LVal();
        lVal.addNode(nowWord);
        getWord();

        if (nowWord.getType().equals(WordType.LBRACK)) {
            ArrayPFix arrayPFix = ArrayPFix();
            lVal.addNode(arrayPFix);
        }

        if (nowWord.getType().equals(WordType.POINT)) {
            ClassPFix classPFix = ClassPFix();
            lVal.addNode(classPFix);
        }

        return lVal;
    }

    //ArrayPFix ::= '[' <Exp> {',' <Exp>} ']'
    public ArrayPFix ArrayPFix() {
        ArrayPFix arrayPFix = new ArrayPFix();
        arrayPFix.addNode(nowWord);
        getWord();

        Exp exp = Exp();
        arrayPFix.addNode(exp);

        while (nowWord.getType().equals(WordType.COMMA)) {
            arrayPFix.addNode(nowWord);
            getWord();

            Synt exp1 = Exp();
            arrayPFix.addNode(exp1);
        }

        arrayPFix.addNode(nowWord);
        getWord();

        return arrayPFix;
    }

    //ClassPFix ::= '.' <LVal>
    public ClassPFix ClassPFix() {
        ClassPFix classPFix = new ClassPFix();
        classPFix.addNode(nowWord);
        getWord();

        LVal lVal = LVal();
        classPFix.addNode(lVal);

        return classPFix;
    }

    //PrimaryExp ::= '(' <Exp> ')' | <Number> | <Character> | <String> | <TF>
    public PrimaryExp PrimaryExp() {
        PrimaryExp primaryExp = new PrimaryExp();
        if (nowWord.getType().equals(WordType.LPARENT)) {
            primaryExp.addNode(nowWord);
            getWord();
            Exp exp = Exp();
            primaryExp.addNode(exp);
            primaryExp.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.NUMBER)) {
            primaryExp.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.CHARCON)) {
            primaryExp.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.STRCON)) {
            primaryExp.addNode(nowWord);
            getWord();
        } else if (nowWord.getType().equals(WordType.TRUETK) || nowWord.getType().equals(WordType.FALSETK)) {
            primaryExp.addNode(nowWord);
            getWord();
        }

        return primaryExp;
    }

    //UnaryExp ::= <PrimaryExp> |
    //                      <ComplexUnaryExp> |
    //                      <UnaryOp> <UnaryExp>
    public UnaryExp UnaryExp() {
        UnaryExp unaryExp = new UnaryExp();
        if (nowWord.getType().equals(WordType.IDENFR)) {
            ComplexUnaryExp complexUnaryExp = ComplexUnaryExp();
            unaryExp.addNode(complexUnaryExp);
        } else if (nowWord.getType().equals(WordType.ADD) ||
                nowWord.getType().equals(WordType.MINU) ||
                nowWord.getType().equals(WordType.NOT)) {
            UnaryOp unaryOp = UnaryOp();
            unaryExp.addNode(unaryOp);
            UnaryExp unaryExp1 = UnaryExp();
            unaryExp.addNode(unaryExp1);
        } else {
            PrimaryExp primaryExp = PrimaryExp();
            unaryExp.addNode(primaryExp);
        }

        return unaryExp;
    }

    //ComplexUnaryExp ::= <LVal> [<ComplexPFix>]
    public ComplexUnaryExp ComplexUnaryExp() {
        ComplexUnaryExp complexUnaryExp = new ComplexUnaryExp();

        LVal lVal = LVal();
        complexUnaryExp.addNode(lVal);

        if (nowWord.getType().equals(WordType.LPARENT)) {
            ComplexPFix complexPFix = ComplexPFix();
            complexUnaryExp.addNode(complexPFix);
        }

        return complexUnaryExp;
    }

    //ComplexPFix ::= <FuncPFix> [<ArrayPFix>] [<ClassPFix>] [<ComplexPFix>]
    public ComplexPFix ComplexPFix() {
        ComplexPFix complexPFix = new ComplexPFix();

        FuncPFix funcPFix = FuncPFix();
        complexPFix.addNode(funcPFix);

        if (nowWord.getType().equals(WordType.LBRACK)) {
            ArrayPFix arrayPFix = ArrayPFix();
            complexPFix.addNode(arrayPFix);
        }

        if (nowWord.getType().equals(WordType.POINT)) {
            ClassPFix classPFix = ClassPFix();
            complexPFix.addNode(classPFix);
        }

        if (nowWord.getType().equals(WordType.LPARENT)) {
            ComplexPFix complexPFix1 = ComplexPFix();
            complexPFix.addNode(complexPFix1);
        }

        return complexPFix;
    }

    //FuncPFix ::= '(' [<FuncRParams>] ')' {'(' [<FuncRParams>] ')'}
    public FuncPFix FuncPFix() {
        FuncPFix funcPFix = new FuncPFix();

        funcPFix.addNode(nowWord);
        getWord();

        if (!nowWord.getType().equals(WordType.RPARENT)) {
            FuncRParams funcRParams = FuncRParams();
            funcPFix.addNode(funcRParams);
        }

        funcPFix.addNode(nowWord);
        getWord();

        while (nowWord.getType().equals(WordType.LPARENT)) {
            funcPFix.addNode(nowWord);
            getWord();

            if (!nowWord.getType().equals(WordType.RPARENT)) {
                FuncRParams funcRParams1 = FuncRParams();
                funcPFix.addNode(funcRParams1);
            }

            funcPFix.addNode(nowWord);
            getWord();
        }

        return funcPFix;
    }

    //UnaryOp ::= '+' | '-' | '!'
    public UnaryOp UnaryOp() {
        UnaryOp unaryOp = new UnaryOp();
        unaryOp.addNode(nowWord);
        getWord();

        return unaryOp;
    }

    //FuncRParams ::= <Exp> {',' <Exp>}
    public FuncRParams FuncRParams() {
        FuncRParams funcRParams = new FuncRParams();

        Exp exp = Exp();
        funcRParams.addNode(exp);

        while (nowWord.getType().equals(WordType.COMMA)) {
            funcRParams.addNode(nowWord);
            getWord();

            Exp exp1 = Exp();
            funcRParams.addNode(exp1);
        }

        return funcRParams;
    }

    //MulExp ::= <UnaryExp> | <MulExp> ('*' | '/' | '%') <UnaryExp>
    public MulExp MulExp() {
        MulExp mulExp = new MulExp();

        UnaryExp unaryExp = UnaryExp();
        mulExp.addNode(unaryExp);

        while (nowWord.getType().equals(WordType.MULT) || nowWord.getType().equals(WordType.DIV) ||
                nowWord.getType().equals(WordType.MOD)) {
            MulExp mulExp1 = mulExp;
            mulExp = new MulExp();
            mulExp.addNode(mulExp1);
            mulExp.addNode(nowWord);
            getWord();
            UnaryExp unaryExp1 = UnaryExp();
            mulExp.addNode(unaryExp1);
        }

        return mulExp;
    }

    //AddExp ::= <MulExp> | <AddExp> ('+' | '-') <MulExp>
    public AddExp AddExp() {
        AddExp addExp = new AddExp();

        MulExp mulExp = MulExp();
        addExp.addNode(mulExp);

        while (nowWord.getType().equals(WordType.ADD) || nowWord.getType().equals(WordType.MINU)) {
            AddExp addExp1 = addExp;
            addExp = new AddExp();
            addExp.addNode(addExp1);
            addExp.addNode(nowWord);
            getWord();
            MulExp mulExp1 = MulExp();
            addExp.addNode(mulExp1);
        }

        return addExp;
    }

    //RelExp ::= <AddExp> | <RelExp> ('<' | '>' | '<=' | '>=') <AddExp>
    public RelExp RelExp() {
        RelExp relExp = new RelExp();

        AddExp addExp = AddExp();
        relExp.addNode(addExp);

        while (nowWord.getType().equals(WordType.LSS) || nowWord.getType().equals(WordType.GRE) ||
                nowWord.getType().equals(WordType.LEQ) || nowWord.getType().equals(WordType.GEQ)) {
            RelExp relExp1 = relExp;
            relExp = new RelExp();
            relExp.addNode(relExp1);
            relExp.addNode(nowWord);
            getWord();
            AddExp addExp1 = AddExp();
            relExp.addNode(addExp1);
        }

        return relExp;
    }

    //EqExp ::= <RelExp> | <EqExp> ('==' | '!=') <RelExp>
    public EqExp EqExp() {
        EqExp eqExp = new EqExp();

        RelExp relExp = RelExp();
        eqExp.addNode(relExp);

        while (nowWord.getType().equals(WordType.EQL) || nowWord.getType().equals(WordType.NEQ)) {
            EqExp eqExp1 = eqExp;
            eqExp = new EqExp();
            eqExp.addNode(eqExp1);
            eqExp.addNode(nowWord);
            getWord();
            RelExp relExp1 = RelExp();
            eqExp.addNode(relExp1);
        }

        return eqExp;
    }

    //LAndExp ::= <EqExp> | <LAndExp> '&&' <EqExp>
    public LAndExp LAndExp() {
        LAndExp lAndExp = new LAndExp();

        EqExp eqExp = EqExp();
        lAndExp.addNode(eqExp);

        while (nowWord.getType().equals(WordType.AND)) {
            LAndExp lAndExp1 = lAndExp;
            lAndExp = new LAndExp();
            lAndExp.addNode(lAndExp1);
            lAndExp.addNode(nowWord);
            getWord();
            EqExp eqExp1 = EqExp();
            lAndExp.addNode(eqExp1);
        }

        return lAndExp;
    }

    //LOrExp ::= <LAndExp> | <LOrExp> '||' <LAndExp>
    public LOrExp LOrExp() {
        LOrExp lOrExp = new LOrExp();

        LAndExp lAndExp = LAndExp();
        lOrExp.addNode(lAndExp);

        while (nowWord.getType().equals(WordType.OR)) {
            LOrExp lOrExp1 = lOrExp;
            lOrExp = new LOrExp();
            lOrExp.addNode(lOrExp1);
            lOrExp.addNode(nowWord);
            getWord();
            LAndExp lAndExp1 = LAndExp();
            lOrExp.addNode(lAndExp1);
        }

        return lOrExp;
    }
}
